The goal of this project is to investigate how partnerships involving multiple top-tier players in the NBA impacts various performance measures and team outcomes. Among the research questions we would like to explore are the following:
[ INSERT RESEARCH QUESTIONS HERE ]
To be able to investigate, we need to pull data from multiple NBA seasons. The script below provides code to create functions that pull traditional stats for every player for a given user-defined season.
Load necessary libraries
<<<<<<< Updated upstreamlibrary(rvest)
library(dplyr)
library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats 1.0.0 ✔ readr 2.1.5
✔ ggplot2 3.5.1 ✔ stringr 1.5.1
✔ lubridate 1.9.3 ✔ tibble 3.2.1
✔ purrr 1.0.2 ✔ tidyr 1.3.1
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ readr::guess_encoding() masks rvest::guess_encoding()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
=======
library(rvest)
library(dplyr)
library(tidyverse)
library(httr)
>>>>>>> Stashed changes
Function to get NBA roster for a specified year
get_nba_roster <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
# Read the HTML content from the URL
webpage <- read_html(url)
# Extract the table containing the player statistics
roster_table <- webpage %>%
html_node("table#per_game_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
roster_table <- roster_table %>%
filter(Player != "Player")
return(roster_table)
}
Example usage
year <- 2018 # Specify the year
nba_roster <- get_nba_roster(year)
#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)
NA
#Summary statistics
position_roster<-filter(nba_roster,Pos!="PG" )
position_roster
library(plotly)
# Assuming 'nba_roster' is your data frame
input <- nba_roster[, c('MP', 'PTS', 'Player','Pos')]
# Create the plotly scatter plot
fig <- plot_ly(input, x = ~MP, y = ~PTS, type = 'scatter', mode = 'markers',
text = ~Player, # This adds player names on hover
hoverinfo = 'text', # Ensures that only player names appear on hover
color = ~Pos, # Colors points based on position
marker = list(size = 10))
# Set the plot title and axis labels
fig <- fig %>% layout(title = "Minutes Played vs Points Scored",
xaxis = list(title = "Minutes Played", range = c(0, 48)),
yaxis = list(title = "Points", range = c(0, 35)))
# Show the plot
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
#Data Visualization for Minutes Played vs Points Scored
input <- nba_roster[, c('MP', 'PTS')]
print(head(input))
# Get the input values.
input <- nba_roster[, c('MP', 'PTS')]
# Plot the chart for cars with
# weight between 1.5 to 4 and
# mileage between 10 and 25.
plot(x = input$MP, y = input$PTS,
xlab = "Minutes Played",
ylab = "Points",
xlim = c(0.0, 48),
ylim = c(0.0, 35),
main = "Minutes Played vs Points Scored"
)
#Data Visualization for Field Goals Attempled vs Field Goals Made
input_2 <- nba_roster[, c('FGA', 'FG')]
print(head(input_2))
# Get the input values.
input_2 <- nba_roster[, c('FGA', 'FG')]
# Plot the chart for players with
# field goal attempts between 0.0 to 25.0 and
b_FG<-max(input_2$FG,na.rm=T)
b_FGA<-max(input_2$FGA,na.rm=T)
# Field Goal Made between 0.0 and 25.0
plot(x = input_2$FGA, y = input_2$FG,
xlab = "Field Goal Attempts",
ylab = "Field Goal Made",
xlim = c(0.0, b_FGA),
ylim = c(0.0, b_FG),
main = "Field Goal Attempt vs Field Goal Made"
)
NA
NA
# Create the data for the chart
A <- c(nba_roster$PTS)
B <- c("PF", "PG", "SF", "C", "SG")
# Plot the bar chart
ggplot(nba_roster, aes(x=Pos, fill=PTS))+
geom_bar()+
theme_classic(16)+
xlab("Position")+
ylab("Points")
Warning: The following aesthetics were dropped during statistical transformation: fill.
ℹ This can happen when ggplot fails to infer the correct grouping structure in the data.
ℹ Did you forget to specify a `group` aesthetic or to convert a numerical variable into a factor?
ASSIGNMENT 1: Is the data “clean”? Are there any missing values to be accounted for/addressed? If there are any data quality issues,
Initial thought to change the default character to double given that we have fractioned values. i think the columns should be changed from ” chararcter” to “double”
b. justify the validity of your approach removing observations with missing data from the dataset, using the function “na.omit” which will remove rows with missing values from our dataset
c. implement your proposed changes
For players who are missing data
nba_roster<-na.omit(nba_roster)
nba_roster
# Convert specific columns from character to double
# Convert all character columns to double
nba_roster %>%
mutate(across(G:PTS, as.numeric))
NA
NA
NA
To determine whether a player is “top tier” and should be considered a part of a “Big 3” lineup, other authors have transformed traditional stats to create metrics such as
PRA = POINTS + REBOUNDS + ASSISTS
We will consider advanced statistics such as PLAYER EFFIFIENCY RATING:
PER = (PTS + REB + AST + STL + BLK − Missed FG − Missed FT - TO) /GP
In particular, Value over Replacement (VORP) seems to do a solid job of identifying the best players in the league.
The script below provide code to create functions that pull advanced stats for every player for a given user-defined season.
# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
# Read the HTML content from the URL
webpage <- read_html(url)
# Extract the table containing the advanced player statistics
advanced_stats_table <- webpage %>%
html_node("table#advanced_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
# advanced_stats_table <- advanced_stats_table %>%
# filter(Player != "Player")
return(advanced_stats_table)
}
# Example usage
year <- 2018 # Specify the year
nba_advanced_stats <- get_nba_advanced_stats(year)
# Print the first few rows of the advanced stats
head(nba_advanced_stats)
NA
NA
ASSIGNMENT 2: Is the advanced data “clean”? Are there any missing values to be accounted for/addressed? If there are any data quality issues,
a. propose a method to resolve them
b. justify the validity of your approach
c. implement your proposed changes
cleaning similar to first one
The script below provide code to clean out the quality issues presented in the dataframe
#1 We want to order the athletes name to alphabetical order to clean out the filler headers present
newdataframe<- dataframe[order(dataframe$Player)]
Error: object 'dataframe' not found
#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats <- nba_advanced_stats[order(nba_advanced_stats$Player),]
# remove filler rows that had been previously used as headers on webpage
AO_nba_advanced_stats<- AO_nba_advanced_stats[-c(502:526), ]
#remove na from dataframe
AO_nba_advanced_stats %>%
select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-20]
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-24]
#change range of cloumns <dbl> from <chr>
AO_nba_advanced_stats %>%
mutate(across(G:VORP, as.numeric))
Warning: There were 22 warnings in `mutate()`.
The first warning was:
ℹ In argument: `across(G:VORP, as.numeric)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 21 remaining warnings.
ASSIGNMENT 3: Merge the cleaned up datasets to create one new data frame with the traditional and advanced stats.
<<<<<<< Updated upstream#how to merge two files into one new data frame
nba_merge<-merge(nba_roster, AO_nba_advanced_stats, by = c("Rk", "Player", "Pos","Age", "Tm","G"))#, by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
=======
#Get NBA Totals Statistics
get_nba_totals_stats <- function(year) {
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_totals.html")
webpage <- read_html(url)
totals_stats_table <- webpage %>%
html_node("table#totals_stats") %>%
html_table(fill = TRUE)
# Clean up column names
colnames(totals_stats_table) <- make.names(colnames(totals_stats_table), unique = TRUE)
# Clean the data
totals_stats_table <- totals_stats_table %>%
filter(!is.na(Player) & Player != "Player") # Ensure no NA or duplicate header rows
return(totals_stats_table)
}
get_nba_advanced_stats <- function(year) {
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
# Fetch webpage
webpage <- tryCatch({
read_html(GET(url, user_agent("Mozilla/5.0")))
}, error = function(e) {
stop("Error fetching webpage: ", e$message)
})
# Extract table with updated ID
advanced_stats_table <- webpage %>%
html_node("table#advanced") %>% # Updated selector to match the new ID
html_table(fill = TRUE)
# Clean up column names
colnames(advanced_stats_table) <- make.names(colnames(advanced_stats_table), unique = TRUE)
# Clean the data
advanced_stats_table <- advanced_stats_table %>%
filter(!is.na(Player) & Player != "Player") # Remove NA rows and duplicate headers
return(advanced_stats_table)
}
>>>>>>> Stashed changes
Error in fix.by(by.x, x) : 'by' must specify a uniquely valid column
ASSIGNMENT 4: Make a function with argument
year that outputs one dataframe with the merged traditional
and advanced data.
<<<<<<< Updated upstream
combined_nba_stats<-function(year){
get_nba_roster2 <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
# Read the HTML content from the URL
webpage <- read_html(url)
=======
#Get NBA Totals Statistics
get_nba_totals_stats <- function(year) {
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_totals.html")
webpage <- read_html(url)
totals_stats_table <- webpage %>%
html_node("table#totals_stats") %>%
html_table(fill = TRUE)
# Clean up column names
colnames(totals_stats_table) <- make.names(colnames(totals_stats_table), unique = TRUE)
# Clean the data
totals_stats_table <- totals_stats_table %>%
filter(!is.na(Player) & Player != "Player") # Ensure no NA or duplicate header rows
return(totals_stats_table)
}
get_nba_advanced_stats <- function(year) {
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
# Fetch webpage
webpage <- tryCatch({
read_html(GET(url, user_agent("Mozilla/5.0")))
}, error = function(e) {
stop("Error fetching webpage: ", e$message)
})
# Extract table with updated ID
advanced_stats_table <- webpage %>%
html_node("table#advanced") %>% # Updated selector to match the new ID
html_table(fill = TRUE)
# Clean up column names
colnames(advanced_stats_table) <- make.names(colnames(advanced_stats_table), unique = TRUE)
# Clean the data
advanced_stats_table <- advanced_stats_table %>%
filter(!is.na(Player) & Player != "Player") # Remove NA rows and duplicate headers
return(advanced_stats_table)
}
get_cleaned_nba_stats <- function(year) {
# Fetch totals and advanced stats
nba_totals <- get_nba_totals_stats(year)
nba_advanced <- get_nba_advanced_stats(year)
# Print to check datasets (Optional)
print("NBA Totals:")
print(head(nba_totals))
print("NBA Advanced:")
print(head(nba_advanced))
>>>>>>> Stashed changes
# Extract the table containing the player statistics
roster_table <- webpage %>%
html_node("table#per_game_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
roster_table <- roster_table %>%
filter(Player != "Player")
return(roster_table)
}
year <- 2023 # Specify the year
nba_roster2 <- get_nba_roster2(year)
#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)
<<<<<<< Updated upstream
#take out the N/A
nba_roster2<-na.omit(nba_roster2)
=======
# Debug: Show merged data sample
print("Merged Data Sample:")
print(head(nba_merge))
# Check column names to confirm 'Team' exists
print("Column Names Before Renaming:")
print(colnames(nba_merge))
# Clean 'Team' column: rename 'Tm' to 'Team' if present
if ("Tm" %in% colnames(nba_merge)) {
nba_merge <- nba_merge %>%
rename(Team = Tm)
}
# Debug: Show column names after renaming
print("Column Names After Renaming 'Tm' to 'Team':")
print(colnames(nba_merge))
# Handle duplicate columns like Team.x, Team.y, Awards.x, Awards.y
duplicate_columns <- colnames(nba_merge)[grepl("\\.x$", colnames(nba_merge))]
for (col in duplicate_columns) {
# Extract the base name of the column (e.g., "Team" from "Team.x")
base_col <- sub("\\.x$", "", col)
# Merge the .x and .y columns into one
if (paste0(base_col, ".y") %in% colnames(nba_merge)) {
nba_merge <- nba_merge %>%
mutate(!!base_col := coalesce(get(col), get(paste0(base_col, ".y")))) %>%
select(-all_of(c(col, paste0(base_col, ".y")))) # Drop the old columns
}
}
>>>>>>> Stashed changes
# Convert specific columns from character to double
<<<<<<< Updated upstream
nba_roster2 %>%
mutate(across(G:PTS, as.numeric))
#ADVANCED STATS
# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
# Construct the URL for the specified year
url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
# Read the HTML content from the URL
webpage <- read_html(url)
# Extract the table containing the advanced player statistics
advanced_stats_table <- webpage %>%
html_node("table#advanced_stats") %>%
html_table(fill = TRUE)
# Clean the data (remove header rows that might be duplicated)
# advanced_stats_table <- advanced_stats_table %>%
# filter(Player != "Player")
return(advanced_stats_table)
}
# Example usage
year <- 2023 # Specify the year
nba_advanced_stats2<- get_nba_advanced_stats(year)
# Print the first few rows of the advanced stats
head(nba_advanced_stats2)
#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats2<- nba_advanced_stats2[order(nba_advanced_stats2$Player),]
#remove na from dataframe
AO_nba_advanced_stats2 %>%
select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-20]
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-24]
# remove filler rows that had been previously used as headers on webpage
AO_nba_advanced_stats2 <- AO_nba_advanced_stats2[AO_nba_advanced_stats2$Player != "Player",]
AO_nba_advanced_stats2$Player <- factor(AO_nba_advanced_stats2$Player)
#change range of cloumns <dbl> from <chr>
AO_nba_advanced_stats2 %>%
mutate(across(G:VORP, as.numeric))
nba_merge<-merge(nba_roster2, AO_nba_advanced_stats2, by.x = c("Rk", "Player", "Pos","Age", "Tm","G"), by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
}
=======
# Remove the 'X', 'X.1' columns if they exist
columns_to_remove <- c("X", "X.1")
nba_merge <- nba_merge %>%
select(-any_of(columns_to_remove)) # Remove specified columns if they exist
# Merge 'Rk.x' and 'Rk.y' columns
if ("Rk.x" %in% names(nba_merge) & "Rk.y" %in% names(nba_merge)) {
nba_merge <- nba_merge %>%
mutate(Rk = coalesce(as.character(Rk.x), as.character(Rk.y))) %>%
select(-Rk.x, -Rk.y)
}
# Merge 'Age.x' and 'Age.y' columns
if ("Age.x" %in% names(nba_merge) & "Age.y" %in% names(nba_merge)) {
nba_merge <- nba_merge %>%
mutate(Age = coalesce(as.numeric(Age.x), as.numeric(Age.y))) %>%
select(-c(Age.x, Age.y))
}
# Reorder columns for clarity
column_order <- c("Player", "Pos", "Age", "Rk", "G", "MP", "Team")
nba_merge <- nba_merge %>%
select(all_of(column_order), everything())
# Return the cleaned and merged dataset
return(nba_merge)
}
# Example usage
nba_data_2013 <- get_cleaned_nba_stats(2013)
>>>>>>> Stashed changes
Error in value[[3L]](cond) :
Error fetching webpage: could not find function "GET"
<<<<<<< Updated upstream
=======
nba_data_1984<-get_cleaned_nba_stats(1984)
Error in open.connection(x, "rb") :
Could not resolve host: www.basketball-reference.com
>>>>>>> Stashed changes
ASSIGNMENT 5: Make this file more visually
appealng, with headers, bullet points, sections and subsections as you
see fit. You may consider migrating over to Quarto for this
reason.
<<<<<<< Updated upstream
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGludmVzdGlnYXRlIGhvdyBwYXJ0bmVyc2hpcHMgaW52b2x2aW5nIG11bHRpcGxlIHRvcC10aWVyIHBsYXllcnMgaW4gdGhlIE5CQSBpbXBhY3RzIHZhcmlvdXMgcGVyZm9ybWFuY2UgbWVhc3VyZXMgYW5kIHRlYW0gb3V0Y29tZXMuIEFtb25nIHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgd2Ugd291bGQgbGlrZSB0byBleHBsb3JlIGFyZSB0aGUgZm9sbG93aW5nOgoKKlsgSU5TRVJUIFJFU0VBUkNIIFFVRVNUSU9OUyBIRVJFIF0qCgpUbyBiZSBhYmxlIHRvIGludmVzdGlnYXRlLCB3ZSBuZWVkIHRvIHB1bGwgZGF0YSBmcm9tIG11bHRpcGxlIE5CQSBzZWFzb25zLiBUaGUgc2NyaXB0IGJlbG93IHByb3ZpZGVzIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgdHJhZGl0aW9uYWwgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgoKTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpGdW5jdGlvbiB0byBnZXQgTkJBIHJvc3RlciBmb3IgYSBzcGVjaWZpZWQgeWVhcgpgYGB7cn0KZ2V0X25iYV9yb3N0ZXIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KYGBgCgpFeGFtcGxlIHVzYWdlCmBgYHtyfQp5ZWFyIDwtIDIwMTggICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyIDwtIGdldF9uYmFfcm9zdGVyKHllYXIpCgojUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByb3N0ZXIKaGVhZChuYmFfcm9zdGVyKQp0YWlsKG5iYV9yb3N0ZXIpCgpgYGAKYGBge3J9CiNTdW1tYXJ5IHN0YXRpc3RpY3MKCnBvc2l0aW9uX3Jvc3RlcjwtZmlsdGVyKG5iYV9yb3N0ZXIsUG9zIT0iUEciICkKcG9zaXRpb25fcm9zdGVyCmBgYAoKCgpgYGB7cn0KCmxpYnJhcnkocGxvdGx5KQoKIyBBc3N1bWluZyAnbmJhX3Jvc3RlcicgaXMgeW91ciBkYXRhIGZyYW1lCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnLCAnUGxheWVyJywnUG9zJyldCgojIENyZWF0ZSB0aGUgcGxvdGx5IHNjYXR0ZXIgcGxvdApmaWcgPC0gcGxvdF9seShpbnB1dCwgeCA9IH5NUCwgeSA9IH5QVFMsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbWFya2VycycsCiAgICAgICAgICAgICAgIHRleHQgPSB+UGxheWVyLCAgIyBUaGlzIGFkZHMgcGxheWVyIG5hbWVzIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGhvdmVyaW5mbyA9ICd0ZXh0JywgIyBFbnN1cmVzIHRoYXQgb25seSBwbGF5ZXIgbmFtZXMgYXBwZWFyIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGNvbG9yID0gflBvcywgICMgQ29sb3JzIHBvaW50cyBiYXNlZCBvbiBwb3NpdGlvbgogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCkpCgojIFNldCB0aGUgcGxvdCB0aXRsZSBhbmQgYXhpcyBsYWJlbHMKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIHZzIFBvaW50cyBTY29yZWQiLAogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIiwgcmFuZ2UgPSBjKDAsIDQ4KSksCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUG9pbnRzIiwgcmFuZ2UgPSBjKDAsIDM1KSkpCgojIFNob3cgdGhlIHBsb3QKZmlnCgoKYGBgCmBgYHtyfQojRGF0YSBWaXN1YWxpemF0aW9uIGZvciBNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnKV0KcHJpbnQoaGVhZChpbnB1dCkpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dCA8LSBuYmFfcm9zdGVyWywgYygnTVAnLCAnUFRTJyldCgojIFBsb3QgdGhlIGNoYXJ0IGZvciBjYXJzIHdpdGgKIyB3ZWlnaHQgYmV0d2VlbiAxLjUgdG8gNCBhbmQKIyBtaWxlYWdlIGJldHdlZW4gMTAgYW5kIDI1LgpwbG90KHggPSBpbnB1dCRNUCwgeSA9IGlucHV0JFBUUywKCXhsYWIgPSAiTWludXRlcyBQbGF5ZWQiLAoJeWxhYiA9ICJQb2ludHMiLAoJeGxpbSA9IGMoMC4wLCA0OCksCgl5bGltID0gYygwLjAsIDM1KSwJIAoJbWFpbiA9ICJNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkIgopCgpgYGAKCgpgYGB7cn0KI0RhdGEgVmlzdWFsaXphdGlvbiBmb3IgRmllbGQgR29hbHMgQXR0ZW1wbGVkIHZzIEZpZWxkIEdvYWxzIE1hZGUKaW5wdXRfMiA8LSBuYmFfcm9zdGVyWywgYygnRkdBJywgJ0ZHJyldCnByaW50KGhlYWQoaW5wdXRfMikpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dF8yIDwtIG5iYV9yb3N0ZXJbLCBjKCdGR0EnLCAnRkcnKV0KCiMgUGxvdCB0aGUgY2hhcnQgZm9yIHBsYXllcnMgd2l0aAojIGZpZWxkIGdvYWwgYXR0ZW1wdHMgYmV0d2VlbiAwLjAgdG8gMjUuMCBhbmQKYl9GRzwtbWF4KGlucHV0XzIkRkcsbmEucm09VCkKYl9GR0E8LW1heChpbnB1dF8yJEZHQSxuYS5ybT1UKQojIEZpZWxkIEdvYWwgTWFkZSBiZXR3ZWVuIDAuMCBhbmQgMjUuMApwbG90KHggPSBpbnB1dF8yJEZHQSwgeSA9IGlucHV0XzIkRkcsCgl4bGFiID0gIkZpZWxkIEdvYWwgQXR0ZW1wdHMiLAoJeWxhYiA9ICJGaWVsZCBHb2FsIE1hZGUiLAoJeGxpbSA9IGMoMC4wLCBiX0ZHQSksCgl5bGltID0gYygwLjAsIGJfRkcpLAkgCgltYWluID0gIkZpZWxkIEdvYWwgQXR0ZW1wdCB2cyBGaWVsZCBHb2FsIE1hZGUiCikKCgpgYGAKCgoKYGBge3J9CiMgQ3JlYXRlIHRoZSBkYXRhIGZvciB0aGUgY2hhcnQKQSA8LSBjKG5iYV9yb3N0ZXIkUFRTKQpCIDwtIGMoIlBGIiwgIlBHIiwgIlNGIiwgIkMiLCAiU0ciKQoKIyBQbG90IHRoZSBiYXIgY2hhcnQKZ2dwbG90KG5iYV9yb3N0ZXIsIGFlcyh4PVBvcywgZmlsbD1QVFMpKSsgIAoKZ2VvbV9iYXIoKSsgIAoKdGhlbWVfY2xhc3NpYygxNikrICAKCnhsYWIoIlBvc2l0aW9uIikrICAKCnlsYWIoIlBvaW50cyIpIAoKYGBgCgoKKipBU1NJR05NRU5UIDE6KiogKklzIHRoZSBkYXRhICJjbGVhbiI/IEFyZSB0aGVyZSBhbnkgbWlzc2luZyB2YWx1ZXMgdG8gYmUgYWNjb3VudGVkIGZvci9hZGRyZXNzZWQ/IElmIHRoZXJlIGFyZSBhbnkgZGF0YSBxdWFsaXR5IGlzc3VlcywqCgogLSAqYS4gcHJvcG9zZSBhIG1ldGhvZCB0byByZXNvbHZlIHRoZW0qCiAgICAgICAKSW5pdGlhbCB0aG91Z2h0IHRvIGNoYW5nZSB0aGUgZGVmYXVsdCBjaGFyYWN0ZXIgdG8gZG91YmxlIGdpdmVuIHRoYXQgd2UgaGF2ZSBmcmFjdGlvbmVkIHZhbHVlcy4gaSB0aGluayB0aGUgY29sdW1ucyBzaG91bGQgYmUgY2hhbmdlZCBmcm9tICIgY2hhcmFyY3RlciIgdG8gImRvdWJsZSIgCgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCnJlbW92aW5nIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgZGF0YSBmcm9tIHRoZSBkYXRhc2V0LCB1c2luZyB0aGUgZnVuY3Rpb24gIm5hLm9taXQiIHdoaWNoIHdpbGwgcmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBmcm9tIG91ciBkYXRhc2V0CgoKIC0gKmMuIGltcGxlbWVudCB5b3VyIHByb3Bvc2VkIGNoYW5nZXMqCgoKRm9yIHBsYXllcnMgd2hvIGFyZSBtaXNzaW5nIGRhdGEKYGBge3J9Cm5iYV9yb3N0ZXI8LW5hLm9taXQobmJhX3Jvc3RlcikKCm5iYV9yb3N0ZXIKCgoKCiMgQ29udmVydCBzcGVjaWZpYyBjb2x1bW5zIGZyb20gY2hhcmFjdGVyIHRvIGRvdWJsZQojIENvbnZlcnQgYWxsIGNoYXJhY3RlciBjb2x1bW5zIHRvIGRvdWJsZQpuYmFfcm9zdGVyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6UFRTLCBhcy5udW1lcmljKSkKCgoKYGBgCgpUbyBkZXRlcm1pbmUgd2hldGhlciBhIHBsYXllciBpcyAidG9wIHRpZXIiIGFuZCBzaG91bGQgYmUgY29uc2lkZXJlZCBhIHBhcnQgb2YgYSAiQmlnIDMiIGxpbmV1cCwgb3RoZXIgYXV0aG9ycyBoYXZlIHRyYW5zZm9ybWVkIHRyYWRpdGlvbmFsIHN0YXRzIHRvIGNyZWF0ZSBtZXRyaWNzIHN1Y2ggYXMKClBSQSA9IFBPSU5UUyArIFJFQk9VTkRTICsgQVNTSVNUUyAKCldlIHdpbGwgY29uc2lkZXIgYWR2YW5jZWQgc3RhdGlzdGljcyBzdWNoIGFzIFBMQVlFUiBFRkZJRklFTkNZIFJBVElORzoKClBFUiA9IChQVFMgKyBSRUIgKyBBU1QgKyBTVEwgKyBCTEsg4oiSIE1pc3NlZCBGRyDiiJIgTWlzc2VkIEZUIC0gVE8pIC9HUAoKSW4gcGFydGljdWxhciwgVmFsdWUgb3ZlciBSZXBsYWNlbWVudCAoVk9SUCkgc2VlbXMgdG8gZG8gYSBzb2xpZCBqb2Igb2YgaWRlbnRpZnlpbmcgdGhlIGJlc3QgcGxheWVycyBpbiB0aGUgbGVhZ3VlLgoKVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgYWR2YW5jZWQgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgpgYGB7cn0KCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDE4ICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0cykKCgpgYGAKCioqQVNTSUdOTUVOVCAyOioqICpJcyB0aGUgYWR2YW5jZWQgZGF0YSAiY2xlYW4iPyBBcmUgdGhlcmUgYW55IG1pc3NpbmcgdmFsdWVzIHRvIGJlIGFjY291bnRlZCBmb3IvYWRkcmVzc2VkPyBJZiB0aGVyZSBhcmUgYW55IGRhdGEgcXVhbGl0eSBpc3N1ZXMsKgoKIC0gKmEuIHByb3Bvc2UgYSBtZXRob2QgdG8gcmVzb2x2ZSB0aGVtKgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCgogLSAqYy4gaW1wbGVtZW50IHlvdXIgcHJvcG9zZWQgY2hhbmdlcyoKIAogY2xlYW5pbmcgc2ltaWxhciB0byBmaXJzdCBvbmUgCiAKIAogVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY2xlYW4gb3V0IHRoZSBxdWFsaXR5IGlzc3VlcyBwcmVzZW50ZWQgaW4gdGhlIGRhdGFmcmFtZQogCiAKIApgYGB7cn0KIzEgV2Ugd2FudCB0byBvcmRlciB0aGUgYXRobGV0ZXMgbmFtZSB0byBhbHBoYWJldGljYWwgb3JkZXIgdG8gY2xlYW4gb3V0IHRoZSBmaWxsZXIgaGVhZGVycyBwcmVzZW50CgpuZXdkYXRhZnJhbWU8LSBkYXRhZnJhbWVbb3JkZXIoZGF0YWZyYW1lJFBsYXllcildCgojMiBOb3cgd2Ugd2FudCB0byByZW1vdmUgdGhlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gdXNlZCBhcyBoZWFkZXJzIG9uIHRoZSB3ZWJwYWdlCgpuZXdkYXRhLmZyYW1lPC1kYXRhZnJhbWVbLWMoNTAyOjUyNiksIF0KCiMzIG5vdyB3ZSB3YW50IHRvIHJlbW92ZSBhbGwgdGhlIE4vQXMgZnJvbSB0aGUgZGF0YXNldApkYXRhZnJhbWUgJT4lIAogIHNlbGVjdCh3aGVyZSh+IWFsbChpcy5uYSguKSkpKQpgYGAKIAogCiAKYGBge3J9Cgojd2FudCB0byBvcmRlciBieSBhbHBoYWJldGljIG5hbWUgdG8gbWFrZSBjbGVhbmluZyBvdXQgdGhlIGZpbGxlciBoZWFkZXJzIGZyb20gdGhlIGRhdGFzZXQKQU9fbmJhX2FkdmFuY2VkX3N0YXRzIDwtIG5iYV9hZHZhbmNlZF9zdGF0c1tvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMkUGxheWVyKSxdCgoKCiMgcmVtb3ZlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gcHJldmlvdXNseSB1c2VkIGFzIGhlYWRlcnMgb24gd2VicGFnZQpBT19uYmFfYWR2YW5jZWRfc3RhdHM8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHNbLWMoNTAyOjUyNiksIF0KCgojcmVtb3ZlIG5hIGZyb20gZGF0YWZyYW1lCkFPX25iYV9hZHZhbmNlZF9zdGF0cyAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yMF0KQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yNF0KCgojY2hhbmdlIHJhbmdlIG9mIGNsb3VtbnMgPGRibD4gZnJvbSA8Y2hyPgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCgoKCgpgYGAKCgoqKkFTU0lHTk1FTlQgMzoqKiAqTWVyZ2UgdGhlIGNsZWFuZWQgdXAgZGF0YXNldHMgdG8gY3JlYXRlIG9uZSBuZXcgZGF0YSBmcmFtZSB3aXRoIHRoZSB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgc3RhdHMuKgoKCgpgYGB7cn0KI2hvdyB0byBtZXJnZSB0d28gZmlsZXMgaW50byBvbmUgbmV3IGRhdGEgZnJhbWUKbmJhX21lcmdlPC1tZXJnZShuYmFfcm9zdGVyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMsIGJ5LnggPSBjKCJSayIsICJQbGF5ZXIiLCAiUG9zIiwiQWdlIiwgIlRtIiwiRyIpLCBieS55ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSAsIGFsbC54ID0gVFJVRSwgYWxsLnkgPSBUUlVFKQoKCmhlYWQobmJhX21lcmdlKQpgYGAKCgoqKkFTU0lHTk1FTlQgNDoqKiAqTWFrZSBhIGZ1bmN0aW9uIHdpdGggYXJndW1lbnQgYHllYXJgIHRoYXQgb3V0cHV0cyBvbmUgZGF0YWZyYW1lIHdpdGggdGhlIG1lcmdlZCB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgZGF0YS4qIAoKYGBge3J9Cgpjb21iaW5lZF9uYmFfc3RhdHM8LWZ1bmN0aW9uKHllYXIpewpnZXRfbmJhX3Jvc3RlcjIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KICAKICB5ZWFyIDwtIDIwMjMgICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyMiA8LSBnZXRfbmJhX3Jvc3RlcjIoeWVhcikKCiNQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIHJvc3RlcgpoZWFkKG5iYV9yb3N0ZXIpCnRhaWwobmJhX3Jvc3RlcikKCgojdGFrZSBvdXQgdGhlIE4vQSAKbmJhX3Jvc3RlcjI8LW5hLm9taXQobmJhX3Jvc3RlcjIpCgoKIyBDb252ZXJ0IHNwZWNpZmljIGNvbHVtbnMgZnJvbSBjaGFyYWN0ZXIgdG8gZG91YmxlCgpuYmFfcm9zdGVyMiAlPiUKICAgbXV0YXRlKGFjcm9zcyhHOlBUUywgYXMubnVtZXJpYykpCgojQURWQU5DRUQgU1RBVFMKCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDIzICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzMjwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0czIpCgoKCiN3YW50IHRvIG9yZGVyIGJ5IGFscGhhYmV0aWMgbmFtZSB0byBtYWtlIGNsZWFuaW5nIG91dCB0aGUgZmlsbGVyIGhlYWRlcnMgZnJvbSB0aGUgZGF0YXNldApBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC0gbmJhX2FkdmFuY2VkX3N0YXRzMltvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllciksXQoKCgoKCiNyZW1vdmUgbmEgZnJvbSBkYXRhZnJhbWUKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMjwtQU9fbmJhX2FkdmFuY2VkX3N0YXRzMlssLTIwXQpBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC1BT19uYmFfYWR2YW5jZWRfc3RhdHMyWywtMjRdCgoKIyByZW1vdmUgZmlsbGVyIHJvd3MgdGhhdCBoYWQgYmVlbiBwcmV2aW91c2x5IHVzZWQgYXMgaGVhZGVycyBvbiB3ZWJwYWdlCgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiA8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHMyW0FPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyICE9ICJQbGF5ZXIiLF0KCkFPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyIDwtIGZhY3RvcihBT19uYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllcikKCgoKCiNjaGFuZ2UgcmFuZ2Ugb2YgY2xvdW1ucyA8ZGJsPiBmcm9tIDxjaHI+CgpBT19uYmFfYWR2YW5jZWRfc3RhdHMyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCiAgIAogICBuYmFfbWVyZ2U8LW1lcmdlKG5iYV9yb3N0ZXIyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMyLCBieS54ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSwgYnkueSA9IGMoIlJrIiwgIlBsYXllciIsICJQb3MiLCJBZ2UiLCAiVG0iLCJHIikgLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gVFJVRSkKICAgCn0KCgoKYGBgCgoKKipBU1NJR05NRU5UIDU6KiogKk1ha2UgdGhpcyBmaWxlIG1vcmUgdmlzdWFsbHkgYXBwZWFsbmcsIHdpdGggaGVhZGVycywgYnVsbGV0IHBvaW50cywgc2VjdGlvbnMgYW5kIHN1YnNlY3Rpb25zIGFzIHlvdSBzZWUgZml0LiBZb3UgbWF5IGNvbnNpZGVyIG1pZ3JhdGluZyBvdmVyIHRvIFF1YXJ0byBmb3IgdGhpcyByZWFzb24uKgoKCg==
=======
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGludmVzdGlnYXRlIGhvdyBwYXJ0bmVyc2hpcHMgaW52b2x2aW5nIG11bHRpcGxlIHRvcC10aWVyIHBsYXllcnMgaW4gdGhlIE5CQSBpbXBhY3RzIHZhcmlvdXMgcGVyZm9ybWFuY2UgbWVhc3VyZXMgYW5kIHRlYW0gb3V0Y29tZXMuIEFtb25nIHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgd2Ugd291bGQgbGlrZSB0byBleHBsb3JlIGFyZSB0aGUgZm9sbG93aW5nOgoKKlsgSU5TRVJUIFJFU0VBUkNIIFFVRVNUSU9OUyBIRVJFIF0qCgpUbyBiZSBhYmxlIHRvIGludmVzdGlnYXRlLCB3ZSBuZWVkIHRvIHB1bGwgZGF0YSBmcm9tIG11bHRpcGxlIE5CQSBzZWFzb25zLiBUaGUgc2NyaXB0IGJlbG93IHByb3ZpZGVzIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgdHJhZGl0aW9uYWwgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgoKTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShodHRyKQoKYGBgCgpGdW5jdGlvbiB0byBnZXQgTkJBIHJvc3RlciBmb3IgYSBzcGVjaWZpZWQgeWVhcgoKCkV4YW1wbGUgdXNhZ2UKCgoKCgoqKkFTU0lHTk1FTlQgMToqKiAqSXMgdGhlIGRhdGEgImNsZWFuIj8gQXJlIHRoZXJlIGFueSBtaXNzaW5nIHZhbHVlcyB0byBiZSBhY2NvdW50ZWQgZm9yL2FkZHJlc3NlZD8gSWYgdGhlcmUgYXJlIGFueSBkYXRhIHF1YWxpdHkgaXNzdWVzLCoKCiAtICphLiBwcm9wb3NlIGEgbWV0aG9kIHRvIHJlc29sdmUgdGhlbSoKICAgICAgIApJbml0aWFsIHRob3VnaHQgdG8gY2hhbmdlIHRoZSBkZWZhdWx0IGNoYXJhY3RlciB0byBkb3VibGUgZ2l2ZW4gdGhhdCB3ZSBoYXZlIGZyYWN0aW9uZWQgdmFsdWVzLiBpIHRoaW5rIHRoZSBjb2x1bW5zIHNob3VsZCBiZSBjaGFuZ2VkIGZyb20gIiBjaGFyYXJjdGVyIiB0byAiZG91YmxlIiAKCgogLSAqYi4ganVzdGlmeSB0aGUgdmFsaWRpdHkgb2YgeW91ciBhcHByb2FjaCoKcmVtb3Zpbmcgb2JzZXJ2YXRpb25zIHdpdGggbWlzc2luZyBkYXRhIGZyb20gdGhlIGRhdGFzZXQsIHVzaW5nIHRoZSBmdW5jdGlvbiAibmEub21pdCIgd2hpY2ggd2lsbCByZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzIGZyb20gb3VyIGRhdGFzZXQKCgogLSAqYy4gaW1wbGVtZW50IHlvdXIgcHJvcG9zZWQgY2hhbmdlcyoKCgoqKkFTU0lHTk1FTlQgMjoqKiAqSXMgdGhlIGFkdmFuY2VkIGRhdGEgImNsZWFuIj8gQXJlIHRoZXJlIGFueSBtaXNzaW5nIHZhbHVlcyB0byBiZSBhY2NvdW50ZWQgZm9yL2FkZHJlc3NlZD8gSWYgdGhlcmUgYXJlIGFueSBkYXRhIHF1YWxpdHkgaXNzdWVzLCoKCiAtICphLiBwcm9wb3NlIGEgbWV0aG9kIHRvIHJlc29sdmUgdGhlbSoKCiAtICpiLiBqdXN0aWZ5IHRoZSB2YWxpZGl0eSBvZiB5b3VyIGFwcHJvYWNoKgoKIC0gKmMuIGltcGxlbWVudCB5b3VyIHByb3Bvc2VkIGNoYW5nZXMqCiAKIGNsZWFuaW5nIHNpbWlsYXIgdG8gZmlyc3Qgb25lIAogCiAKIFRoZSBzY3JpcHQgYmVsb3cgcHJvdmlkZSBjb2RlIHRvIGNsZWFuIG91dCB0aGUgcXVhbGl0eSBpc3N1ZXMgcHJlc2VudGVkIGluIHRoZSBkYXRhZnJhbWUKIAogCiAKYGBge3J9CgpgYGAKIAogCiAKYGBge3J9CgpgYGAKCgoqKkFTU0lHTk1FTlQgMzoqKiAqTWVyZ2UgdGhlIGNsZWFuZWQgdXAgZGF0YXNldHMgdG8gY3JlYXRlIG9uZSBuZXcgZGF0YSBmcmFtZSB3aXRoIHRoZSB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgc3RhdHMuKgoKCgpgYGB7cn0KI0dldCBOQkEgVG90YWxzIFN0YXRpc3RpY3MKZ2V0X25iYV90b3RhbHNfc3RhdHMgPC0gZnVuY3Rpb24oeWVhcikgewogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3RvdGFscy5odG1sIikKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgdG90YWxzX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI3RvdGFsc19zdGF0cyIpICU+JQogICAgaHRtbF90YWJsZShmaWxsID0gVFJVRSkKCiAgIyBDbGVhbiB1cCBjb2x1bW4gbmFtZXMKICBjb2xuYW1lcyh0b3RhbHNfc3RhdHNfdGFibGUpIDwtIG1ha2UubmFtZXMoY29sbmFtZXModG90YWxzX3N0YXRzX3RhYmxlKSwgdW5pcXVlID0gVFJVRSkKCiAgIyBDbGVhbiB0aGUgZGF0YQogIHRvdGFsc19zdGF0c190YWJsZSA8LSB0b3RhbHNfc3RhdHNfdGFibGUgJT4lCiAgICBmaWx0ZXIoIWlzLm5hKFBsYXllcikgJiBQbGF5ZXIgIT0gIlBsYXllciIpICAjIEVuc3VyZSBubyBOQSBvciBkdXBsaWNhdGUgaGVhZGVyIHJvd3MKICAKICByZXR1cm4odG90YWxzX3N0YXRzX3RhYmxlKQp9CgoKZ2V0X25iYV9hZHZhbmNlZF9zdGF0cyA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2xlYWd1ZXMvTkJBXyIsIHllYXIsICJfYWR2YW5jZWQuaHRtbCIpCiAgCiAgIyBGZXRjaCB3ZWJwYWdlCiAgd2VicGFnZSA8LSB0cnlDYXRjaCh7CiAgICByZWFkX2h0bWwoR0VUKHVybCwgdXNlcl9hZ2VudCgiTW96aWxsYS81LjAiKSkpCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgICBzdG9wKCJFcnJvciBmZXRjaGluZyB3ZWJwYWdlOiAiLCBlJG1lc3NhZ2UpCiAgfSkKICAKICAjIEV4dHJhY3QgdGFibGUgd2l0aCB1cGRhdGVkIElECiAgYWR2YW5jZWRfc3RhdHNfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjYWR2YW5jZWQiKSAlPiUgICMgVXBkYXRlZCBzZWxlY3RvciB0byBtYXRjaCB0aGUgbmV3IElECiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdXAgY29sdW1uIG5hbWVzCiAgY29sbmFtZXMoYWR2YW5jZWRfc3RhdHNfdGFibGUpIDwtIG1ha2UubmFtZXMoY29sbmFtZXMoYWR2YW5jZWRfc3RhdHNfdGFibGUpLCB1bmlxdWUgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEKICBhZHZhbmNlZF9zdGF0c190YWJsZSA8LSBhZHZhbmNlZF9zdGF0c190YWJsZSAlPiUKICAgIGZpbHRlcighaXMubmEoUGxheWVyKSAmIFBsYXllciAhPSAiUGxheWVyIikgICMgUmVtb3ZlIE5BIHJvd3MgYW5kIGR1cGxpY2F0ZSBoZWFkZXJzCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgoKYGBgCgoKKipBU1NJR05NRU5UIDQ6KiogKk1ha2UgYSBmdW5jdGlvbiB3aXRoIGFyZ3VtZW50IGB5ZWFyYCB0aGF0IG91dHB1dHMgb25lIGRhdGFmcmFtZSB3aXRoIHRoZSBtZXJnZWQgdHJhZGl0aW9uYWwgYW5kIGFkdmFuY2VkIGRhdGEuKiAKCgpPZmZpY2lhbCBDbGVhbmluZyBGdW5jdGlvbiB0aGF0IHdvcmtzIGFzIG9mIDEwLzI5LzIwMjQKYGBge3J9CgojR2V0IE5CQSBUb3RhbHMgU3RhdGlzdGljcwpnZXRfbmJhX3RvdGFsc19zdGF0cyA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2xlYWd1ZXMvTkJBXyIsIHllYXIsICJfdG90YWxzLmh0bWwiKQogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKICB0b3RhbHNfc3RhdHNfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjdG90YWxzX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQoKICAjIENsZWFuIHVwIGNvbHVtbiBuYW1lcwogIGNvbG5hbWVzKHRvdGFsc19zdGF0c190YWJsZSkgPC0gbWFrZS5uYW1lcyhjb2xuYW1lcyh0b3RhbHNfc3RhdHNfdGFibGUpLCB1bmlxdWUgPSBUUlVFKQoKICAjIENsZWFuIHRoZSBkYXRhCiAgdG90YWxzX3N0YXRzX3RhYmxlIDwtIHRvdGFsc19zdGF0c190YWJsZSAlPiUKICAgIGZpbHRlcighaXMubmEoUGxheWVyKSAmIFBsYXllciAhPSAiUGxheWVyIikgICMgRW5zdXJlIG5vIE5BIG9yIGR1cGxpY2F0ZSBoZWFkZXIgcm93cwogIAogIHJldHVybih0b3RhbHNfc3RhdHNfdGFibGUpCn0KCgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIEZldGNoIHdlYnBhZ2UKICB3ZWJwYWdlIDwtIHRyeUNhdGNoKHsKICAgIHJlYWRfaHRtbChHRVQodXJsLCB1c2VyX2FnZW50KCJNb3ppbGxhLzUuMCIpKSkKICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICAgIHN0b3AoIkVycm9yIGZldGNoaW5nIHdlYnBhZ2U6ICIsIGUkbWVzc2FnZSkKICB9KQogIAogICMgRXh0cmFjdCB0YWJsZSB3aXRoIHVwZGF0ZWQgSUQKICBhZHZhbmNlZF9zdGF0c190YWJsZSA8LSB3ZWJwYWdlICU+JQogICAgaHRtbF9ub2RlKCJ0YWJsZSNhZHZhbmNlZCIpICU+JSAgIyBVcGRhdGVkIHNlbGVjdG9yIHRvIG1hdGNoIHRoZSBuZXcgSUQKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB1cCBjb2x1bW4gbmFtZXMKICBjb2xuYW1lcyhhZHZhbmNlZF9zdGF0c190YWJsZSkgPC0gbWFrZS5uYW1lcyhjb2xuYW1lcyhhZHZhbmNlZF9zdGF0c190YWJsZSksIHVuaXF1ZSA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YQogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICAgZmlsdGVyKCFpcy5uYShQbGF5ZXIpICYgUGxheWVyICE9ICJQbGF5ZXIiKSAgIyBSZW1vdmUgTkEgcm93cyBhbmQgZHVwbGljYXRlIGhlYWRlcnMKICAKICByZXR1cm4oYWR2YW5jZWRfc3RhdHNfdGFibGUpCn0KCmdldF9jbGVhbmVkX25iYV9zdGF0cyA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgIyBGZXRjaCB0b3RhbHMgYW5kIGFkdmFuY2VkIHN0YXRzCiAgbmJhX3RvdGFscyA8LSBnZXRfbmJhX3RvdGFsc19zdGF0cyh5ZWFyKQogIG5iYV9hZHZhbmNlZCA8LSBnZXRfbmJhX2FkdmFuY2VkX3N0YXRzKHllYXIpCgogICMgUHJpbnQgdG8gY2hlY2sgZGF0YXNldHMgKE9wdGlvbmFsKQogIHByaW50KCJOQkEgVG90YWxzOiIpCiAgcHJpbnQoaGVhZChuYmFfdG90YWxzKSkKICBwcmludCgiTkJBIEFkdmFuY2VkOiIpCiAgcHJpbnQoaGVhZChuYmFfYWR2YW5jZWQpKQoKICAjIENsZWFuIFBsYXllciBuYW1lcyBpbiB0aGUgYWR2YW5jZWQgZGF0YXNldDogcmVtb3ZlIHRoZSBhc3RlcmlzayBhbmQgdHJpbSBzcGFjZXMKICBuYmFfYWR2YW5jZWQgPC0gbmJhX2FkdmFuY2VkICU+JQogICAgbXV0YXRlKFBsYXllciA9IHRyaW13cyhnc3ViKCJcXCoiLCAiIiwgUGxheWVyKSkpICAjIFJlbW92ZSBhc3RlcmlzawoKICAjIEVuc3VyZSB0aGF0IHRoZSBhZHZhbmNlZCBzdGF0cyBjb25zaWRlciB0aGUgY2xlYW5lZCBwbGF5ZXIgbmFtZXMKICBuYmFfYWR2YW5jZWQgPC0gbmJhX2FkdmFuY2VkICU+JQogICAgbXV0YXRlKFBsYXllciA9IHRyaW13cyhQbGF5ZXIpKQoKICAjIE1lcmdlIHRoZSBkYXRhc2V0cyBvbiBQbGF5ZXIsIFBvcywgRywgYW5kIE1QCiAgbmJhX21lcmdlIDwtIG1lcmdlKG5iYV90b3RhbHMsIG5iYV9hZHZhbmNlZCwgCiAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiUGxheWVyIiwgIlBvcyIsICJHIiwgIk1QIiksIAogICAgICAgICAgICAgICAgICAgICBhbGwueCA9IFRSVUUpCgogICMgRGVidWc6IFNob3cgbWVyZ2VkIGRhdGEgc2FtcGxlCiAgcHJpbnQoIk1lcmdlZCBEYXRhIFNhbXBsZToiKQogIHByaW50KGhlYWQobmJhX21lcmdlKSkKICAKICAjIENoZWNrIGNvbHVtbiBuYW1lcyB0byBjb25maXJtICdUZWFtJyBleGlzdHMKICBwcmludCgiQ29sdW1uIE5hbWVzIEJlZm9yZSBSZW5hbWluZzoiKQogIHByaW50KGNvbG5hbWVzKG5iYV9tZXJnZSkpCgogICMgQ2xlYW4gJ1RlYW0nIGNvbHVtbjogcmVuYW1lICdUbScgdG8gJ1RlYW0nIGlmIHByZXNlbnQKICBpZiAoIlRtIiAlaW4lIGNvbG5hbWVzKG5iYV9tZXJnZSkpIHsKICAgIG5iYV9tZXJnZSA8LSBuYmFfbWVyZ2UgJT4lCiAgICAgIHJlbmFtZShUZWFtID0gVG0pCiAgfQoKICAjIERlYnVnOiBTaG93IGNvbHVtbiBuYW1lcyBhZnRlciByZW5hbWluZwogIHByaW50KCJDb2x1bW4gTmFtZXMgQWZ0ZXIgUmVuYW1pbmcgJ1RtJyB0byAnVGVhbSc6IikKICBwcmludChjb2xuYW1lcyhuYmFfbWVyZ2UpKQoKICAjIEhhbmRsZSBkdXBsaWNhdGUgY29sdW1ucyBsaWtlIFRlYW0ueCwgVGVhbS55LCBBd2FyZHMueCwgQXdhcmRzLnkKICBkdXBsaWNhdGVfY29sdW1ucyA8LSBjb2xuYW1lcyhuYmFfbWVyZ2UpW2dyZXBsKCJcXC54JCIsIGNvbG5hbWVzKG5iYV9tZXJnZSkpXQoKICBmb3IgKGNvbCBpbiBkdXBsaWNhdGVfY29sdW1ucykgewogICAgIyBFeHRyYWN0IHRoZSBiYXNlIG5hbWUgb2YgdGhlIGNvbHVtbiAoZS5nLiwgIlRlYW0iIGZyb20gIlRlYW0ueCIpCiAgICBiYXNlX2NvbCA8LSBzdWIoIlxcLngkIiwgIiIsIGNvbCkKICAgIAogICAgIyBNZXJnZSB0aGUgLnggYW5kIC55IGNvbHVtbnMgaW50byBvbmUKICAgIGlmIChwYXN0ZTAoYmFzZV9jb2wsICIueSIpICVpbiUgY29sbmFtZXMobmJhX21lcmdlKSkgewogICAgICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgICAgIG11dGF0ZSghIWJhc2VfY29sIDo9IGNvYWxlc2NlKGdldChjb2wpLCBnZXQocGFzdGUwKGJhc2VfY29sLCAiLnkiKSkpKSAlPiUKICAgICAgICBzZWxlY3QoLWFsbF9vZihjKGNvbCwgcGFzdGUwKGJhc2VfY29sLCAiLnkiKSkpKSAgIyBEcm9wIHRoZSBvbGQgY29sdW1ucwogICAgfQogIH0KCiAgIyBSZW1vdmUgcGxheWVycyB3aG9zZSB0ZWFtIGlzICJUT1QiLCAiMlRtIiwgb3IgIjNUbSIKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgZmlsdGVyKCFncmVwbCgiXihUT1R8MlRNfDNUTSkkIiwgVGVhbSkpCgogICMgUmVtb3ZlIHBsYXllcnMgd2l0aCBtdWx0aXBsZSBwb3NpdGlvbnMKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgZmlsdGVyKCFncmVwbCgiLSIsIFBvcykpCgogICMgUmVtb3ZlIHRoZSAnWCcsICdYLjEnIGNvbHVtbnMgaWYgdGhleSBleGlzdAogIGNvbHVtbnNfdG9fcmVtb3ZlIDwtIGMoIlgiLCAiWC4xIikKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgc2VsZWN0KC1hbnlfb2YoY29sdW1uc190b19yZW1vdmUpKSAgIyBSZW1vdmUgc3BlY2lmaWVkIGNvbHVtbnMgaWYgdGhleSBleGlzdAoKICAjIE1lcmdlICdSay54JyBhbmQgJ1JrLnknIGNvbHVtbnMKICBpZiAoIlJrLngiICVpbiUgbmFtZXMobmJhX21lcmdlKSAmICJSay55IiAlaW4lIG5hbWVzKG5iYV9tZXJnZSkpIHsKICAgIG5iYV9tZXJnZSA8LSBuYmFfbWVyZ2UgJT4lCiAgICAgIG11dGF0ZShSayA9IGNvYWxlc2NlKGFzLmNoYXJhY3RlcihSay54KSwgYXMuY2hhcmFjdGVyKFJrLnkpKSkgJT4lCiAgICAgIHNlbGVjdCgtUmsueCwgLVJrLnkpCiAgfQoKICAjIE1lcmdlICdBZ2UueCcgYW5kICdBZ2UueScgY29sdW1ucwogIGlmICgiQWdlLngiICVpbiUgbmFtZXMobmJhX21lcmdlKSAmICJBZ2UueSIgJWluJSBuYW1lcyhuYmFfbWVyZ2UpKSB7CiAgICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgICBtdXRhdGUoQWdlID0gY29hbGVzY2UoYXMubnVtZXJpYyhBZ2UueCksIGFzLm51bWVyaWMoQWdlLnkpKSkgJT4lCiAgICAgIHNlbGVjdCgtYyhBZ2UueCwgQWdlLnkpKQogIH0KCiAgIyBSZW9yZGVyIGNvbHVtbnMgZm9yIGNsYXJpdHkKICBjb2x1bW5fb3JkZXIgPC0gYygiUGxheWVyIiwgIlBvcyIsICJBZ2UiLCAiUmsiLCAiRyIsICJNUCIsICJUZWFtIikKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgc2VsZWN0KGFsbF9vZihjb2x1bW5fb3JkZXIpLCBldmVyeXRoaW5nKCkpCgogICMgUmV0dXJuIHRoZSBjbGVhbmVkIGFuZCBtZXJnZWQgZGF0YXNldAogIHJldHVybihuYmFfbWVyZ2UpCn0KCiMgRXhhbXBsZSB1c2FnZQpuYmFfZGF0YV8yMDEzIDwtIGdldF9jbGVhbmVkX25iYV9zdGF0cygyMDEzKQoKIyBWaWV3IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgY2xlYW5lZCBkYXRhc2V0CmhlYWQobmJhX2RhdGFfMjAxMykKCgpgYGAKCgpgYGB7cn0KbmJhX2RhdGFfMjAyMiA8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDIyKQpuYmFfZGF0YV8yMDEwIDwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMTApCm5iYV9kYXRhXzIwMTUgPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAxNSkKbmJhX2RhdGFfMjAxMTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMTEpCm5iYV9kYXRhXzIwMTI8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDEyKQpuYmFfZGF0YV8yMDA5PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwOSkKbmJhX2RhdGFfMjAwODwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMDgpCm5iYV9kYXRhXzIwMDc8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDA3KQpuYmFfZGF0YV8yMDA2PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwNikKbmJhX2RhdGFfMjAwNTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMDUpCm5iYV9kYXRhXzIwMDQ8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDA0KQpuYmFfZGF0YV8yMDAzPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwMykKbmJhX2RhdGFfMjAwMjwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMDIpCm5iYV9kYXRhXzIwMDE8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDAxKQpuYmFfZGF0YV8yMDAwPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwMCkKCgoKbmJhX2RhdGFfMTk5OTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTkpCm5iYV9kYXRhXzE5OTg8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTk4KQpuYmFfZGF0YV8xOTk3PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk5NykKbmJhX2RhdGFfMTk5NjwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTYpCm5iYV9kYXRhXzE5OTU8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTk1KQpuYmFfZGF0YV8xOTk0PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk5NCkKbmJhX2RhdGFfMTk5MzwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTMpCm5iYV9kYXRhXzE5OTI8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTkyKQpuYmFfZGF0YV8xOTkxPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk5MSkKbmJhX2RhdGFfMTk5MDwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTApCm5iYV9kYXRhXzE5ODk8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTg5KQpuYmFfZGF0YV8xOTg4PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk4OCkKbmJhX2RhdGFfMTk4NzwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5ODcpCm5iYV9kYXRhXzE5ODY8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTg2KQpuYmFfZGF0YV8xOTg1PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk4NSkKbmJhX2RhdGFfMTk4NDwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5ODQpCm5iYV9kYXRhXzE5ODM8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTgzKQpuYmFfZGF0YV8xOTgyPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk4MikKbmJhX2RhdGFfMTk4MTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5ODEpCm5iYV9kYXRhXzE5ODA8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTgwKQoKcmVhZExpbmVzKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vdGVhbXMvTEFMLzIwMjMuaHRtbCIsIG4gPSAxKQoKYGBgCgoKKipBU1NJR05NRU5UIDU6KiogKk1ha2UgdGhpcyBmaWxlIG1vcmUgdmlzdWFsbHkgYXBwZWFsbmcsIHdpdGggaGVhZGVycywgYnVsbGV0IHBvaW50cywgc2VjdGlvbnMgYW5kIHN1YnNlY3Rpb25zIGFzIHlvdSBzZWUgZml0LiBZb3UgbWF5IGNvbnNpZGVyIG1pZ3JhdGluZyBvdmVyIHRvIFF1YXJ0byBmb3IgdGhpcyByZWFzb24uKgoKCkZpbGUgbG9jYXRvcgpgYGB7cn0KIyBTYXZlIHlvdXIgZGF0YWZyYW1lIGFzIGEgQ1NWIGZpbGUKd3JpdGUuY3N2KG5iYV9yb3N0ZXIyLCBmaWxlID0gImdlbmVyYWxzdGF0cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KEFPX25iYV9hZHZhbmNlZF9zdGF0czIsIGZpbGUgPSAiYWR2YW5jZWRzdGF0cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KG5iYV9kYXRhXzIwMjMsIGZpbGUgPSAibmJhMjAyMy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KG5iYV9kYXRhXzIwMTMsIGZpbGUgPSAibmJhMjAxMy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKZ2V0d2QoKQoKYGBgCgoKCgoK
>>>>>>> Stashed changes